﻿using System;
using System.Threading;

namespace Netbits.Common.Threading
{
	/// <summary>
	/// Represents a counter based condition event handling.
	/// The ManualResetEvent was reseted if the condition is //TODO: beschreibung
	/// </summary>
	public class ConditionEvent : IDisposable
	{
		#region Private Variables

		bool _Disposed;
		readonly int _ConditionValue;
		readonly int _InitialValue;
		ManualResetEventSlim _WaitHandler;

		int _Value;

		#endregion

		#region Constructor

		/// <summary>
		/// Create a new ConditionEvent instance.
		/// </summary>
		/// <param name="ConditionValue_">If this value is reached the event was reseted</param>
		/// <param name="InitialValue_">The initial value.</param>
		public ConditionEvent( int ConditionValue_, int InitialValue_ )
		{
			_ConditionValue = ConditionValue_;
			_Value = _InitialValue = InitialValue_;
			_WaitHandler = new ManualResetEventSlim( _InitialValue != _ConditionValue );
		}

		#endregion

		#region Public Properties

		/// <summary>
		/// Returns the actually value for this condition check.
		/// </summary>
		public int Value
		{
			get
			{
				return _Value;
			}
		}

		/// <summary>
		/// Returns the initial value for this condition.
		/// </summary>
		public int InitialValue
		{
			get
			{
				return _InitialValue;
			}
		}

		/// <summary>
		/// Returns the condition-check value 
		/// </summary>
		public int ConditionValue
		{
			get
			{
				return _ConditionValue;
			}
		}

		/// <summary>
		/// Returns if the Event is set.
		/// </summary>
		public bool IsSet
		{
			get
			{
				return _WaitHandler.IsSet;
			}
		}

		/// <summary>
		/// Returns the instance for this wait-handle.
		/// </summary>
		public WaitHandle WaitHandle
		{
			get
			{
				return _WaitHandler.WaitHandle;
			}
		}

		/// <summary>
		/// Returns the ManualResetEventSlim instance for this condition-check.
		/// </summary>
		public ManualResetEventSlim Event
		{
			get
			{
				return _WaitHandler;
			}
		}

		#endregion

		#region Public Functions

		/// <summary>
		/// Increment the value and make the condition check.
		/// </summary>
		public void Increment()
		{
			_CheckValue( Interlocked.Increment( ref _Value ) );
		}

		/// <summary>
		/// Decrement the value and make the condition check.
		/// </summary>
		public void Decrement()
		{
			_CheckValue( Interlocked.Decrement( ref _Value ) );
		}

		/// <summary>
		/// Add the specified value to the internal value and make the condition check.
		/// </summary>
		/// <param name="Value_">The value to add.</param>
		public void Add( int Value_ )
		{
			_CheckValue( Interlocked.Add( ref _Value, Value_ ) );
		}

		/// <summary>
		/// Subtract the specified value from the internal value and make the condition check.
		/// </summary>
		/// <param name="Value_">The value to subtract</param>
		public void Subtract( int Value_ )
		{
			_CheckValue( Interlocked.Add( ref _Value, Value_ * (-1) ) );
		}

		/// <summary>
		/// Call the WaitHandler-Wait
		/// </summary>
		public void Wait()
		{
			_WaitHandler.Wait();
		}

		/// <summary>
		/// Call the WaitHandler-Wait with specified CancellationToken
		/// </summary>
		/// <param name="Token_">The cancellation-token instance</param>
		public void Wait( CancellationToken Token_ )
		{
			_WaitHandler.Wait( Token_ );
		}

		/// <summary>
		/// Call the WaitHandler-Wait with specified Timeout
		/// </summary>
		/// <param name="MillisecondsTimeout_">The timeout to wait in milliseconds</param>
		public void Wait( int MillisecondsTimeout_ )
		{
			_WaitHandler.Wait( MillisecondsTimeout_ );
		}

		/// <summary>
		/// Call the WaitHander-Wait with specified Timeout
		/// </summary>
		/// <param name="Timeout_">The Timeout to wait.</param>
		public void Wait( TimeSpan Timeout_ )
		{
			_WaitHandler.Wait( Timeout_ );
		}

		/// <summary>
		/// Call the WaitHandler-Wait with the specified Timeout and CancellationToken.
		/// </summary>
		/// <param name="MillisecondsTimeout_">The timeout in milliseconds to wait</param>
		/// <param name="Token_">The instance of CancellationToken</param>
		public void Wait( int MillisecondsTimeout_, CancellationToken Token_ )
		{
			_WaitHandler.Wait( MillisecondsTimeout_, Token_ );
		}

		/// <summary>
		/// Call the WaitHandler-Wait with the specified Timeout and CancellationToken.
		/// </summary>
		/// <param name="Timeout_">The timeout to wait</param>
		/// <param name="Token_">The instance of CancellationToken</param>
		public void Wait( TimeSpan Timeout_, CancellationToken Token_ )
		{
			_WaitHandler.Wait( Timeout_, Token_ );
		}

		/// <summary>
		/// Reset to the initial-value.
		/// </summary>
		public void Reset()
		{
			Reset( _InitialValue );
		}

		/// <summary>
		/// Reset the ConditionEvent to the specified Value.
		/// </summary>
		/// <param name="Value_">The Value reset to.</param>
		public void Reset( int Value_ )
		{
			_CheckValue( Interlocked.Exchange( ref _Value, Value_ ) );
		}

		#endregion

		#region Private Functions

		void _CheckValue( int Value_ )
		{
			if( Value_ == _ConditionValue )
				_WaitHandler.Reset();
			else
				_WaitHandler.Set();
		}

		#endregion

		#region IDisposeable

		/// <summary>
		/// Disposing all resources.
		/// </summary>
		/// <param name="Disposing_">Are Disposing.</param>
		protected virtual void Dispose( bool Disposing_ )
		{
			if( !_Disposed )
			{
				_Disposed = true;
				if( Disposing_ )
				{
					if( _WaitHandler != null )
					{
						_WaitHandler.Dispose();
						_WaitHandler = null;
					}
				}
			}
		}

		/// <summary>
		/// Dispose all internal resources.
		/// </summary>
		public void Dispose()
		{
			Dispose( true );
			GC.SuppressFinalize( this );
		}

		#endregion
	}
}
